package com.zsk.shop.common.socket;

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.zsk.shop.common.base.BaseResponse;
import com.zsk.shop.common.base.BizException;
import com.zsk.shop.common.constant.RedisKeys;
import com.zsk.shop.common.socket.model.socket.WebSocketMessageInfoParams;
import com.zsk.shop.common.socket.model.socket.WebsocketReturnCode;
import com.zsk.shop.common.utils.HttpContext;
import com.zsk.shop.common.utils.RedisUtil;
import com.zsk.shop.common.utils.ToolUtil;
import com.zsk.shop.entity.TokenInfo;
import com.zsk.shop.service.TokenInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zhangws email:1127145966@qq.com
 * @Description:
 * @Date:2020/5/4 0004 11:55
 */
@Slf4j
@Component
@ServerEndpoint(value = "/serviceSocket/link/{token}")
public class ServicePushSocketServer {

    @Autowired
    private TokenInfoService tokenInfoService;

    @PostConstruct
    public void init() {
        log.info("ServiceWebSocket加载");
    }

    public static final AtomicInteger onlineCount = new AtomicInteger(0);
    public static ConcurrentHashMap<String, List<Session>> webSocketMap = new ConcurrentHashMap();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) throws BizException {
        String userNo;
        try {
            userNo = HttpContext.getUserNo(token);
        } catch (Exception e) {
            BaseResponse result = new BaseResponse(WebsocketReturnCode.ERROR.getCode(), "当前登录校验有误");
            sendMessage(session, result);
            throw new BizException("token校验失败，socket连接失败");
        }
        String sessionIdKey = RedisKeys.appUser.getSocketMapKey(userNo);
        List<Session> sessions = webSocketMap.get(sessionIdKey);
        if (ToolUtil.isEmpty(sessions)) {
            sessions = new ArrayList();
        }
        sessions.add(session);
        RedisUtil.setForTimeMS(RedisKeys.appUser.getSocketRedisKey(session.getId()), userNo, 24L * 1L * 3600L * 1000L);
        webSocketMap.put(RedisKeys.appUser.getSocketMapKey(userNo), sessions);
        int cnt = onlineCount.incrementAndGet(); // 在线数加1
        log.info("用户连接:" + userNo + ",当前连接数为:" + cnt);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        String userNo = (String) RedisUtil.get(RedisKeys.appUser.getSocketRedisKey(session.getId()));
        List<Session> sessions = webSocketMap.get(RedisKeys.appUser.getSocketMapKey(userNo));
        if (ToolUtil.isNotEmpty(sessions)) {
            sessions.remove(session);
            RedisUtil.delete(RedisKeys.appUser.getSocketRedisKey(session.getId()));
//            webSocketMap.put(RedisKeys.appUser.getSocketMapKey(userNo), sessions);
            int cnt = onlineCount.decrementAndGet();
            log.info("有连接关闭，当前连接数为：{}", cnt);
        }
    }

    /**
     * 如果重复顶掉及之前内容删除，当前登录用户的session都清除
     *
     * @param userNo
     */
    public void clearUserInfoByUserNo(Long id, String userNo, Integer userType, Boolean isGop) {
        //Token
        List<TokenInfo> tokenInfos = tokenInfoService.queryUnInvalidTokensByUserId(id, userType, isGop);
        for (TokenInfo tokenInfo : tokenInfos) {
            RedisUtil.delete(RedisKeys.appUser.getHashKey(tokenInfo.getToken()));
        }
        tokenInfoService.invalidTokens(tokenInfos);
        //Session
        List<Session> sessions = webSocketMap.get(RedisKeys.appUser.getSocketMapKey(userNo));
        if (ToolUtil.isNotEmpty(sessions)) {
            for (Session session : sessions) {
                RedisUtil.delete(RedisKeys.appUser.getSocketRedisKey(session.getId()));
            }
            webSocketMap.remove(RedisKeys.appUser.getSocketMapKey(userNo));
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        log.info("来自客户端的消息：msg={},sessionId={}", message, session.getId());
        //业务：比方说点播
        WebSocketMessageInfoParams params = null;
        BaseResponse result;
        try {
            params = JSONObject.parseObject(message, WebSocketMessageInfoParams.class);
        } catch (Exception e) {
            result = new BaseResponse(WebsocketReturnCode.ERROR.getCode(), "当前服务端收到数据解析失败");
            sendMessage(session, result);
            //保持连接，返回前端解析错误问题
            return;
        }
        result = BaseResponse.success("服务端收到客户端消息成功", params.getData());
        sendMessage(session, result);
    }

    /**
     * 出现错误
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误：{}，Session ID： {}", error, session.getId());
    }

    public void sendMessage(Session session, BaseResponse message) {
        try {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(JSONObject.toJSONString(message));
            }
        } catch (IOException e) {
            log.error("发送消息出错：{}", e);
        }
    }

    /**
     * 服务端给客户端在不同业务场景下发送消息
     *
     * @param userNo  接受消息的用户
     * @param message 消息内容
     * @return
     * @throws ClientException
     */
    public BaseResponse sendMessage(String userNo, BaseResponse message) throws ClientException {
        List<Session> sessions = webSocketMap.get(RedisKeys.appUser.getSocketMapKey(userNo));
        if (ToolUtil.isEmpty(sessions)) {
            return BaseResponse.error("当前连接已全部断开");
        }
        for (Session sessionGet : sessions) {
            this.sendMessage(sessionGet, message);
            log.info("来自服务端的消息：msg={},sessionId={}", message, sessionGet.getId());
        }
        return BaseResponse.success("当前消息已发送成功");
    }

    public void checkWebSocketMap() {
        log.info("当前webSocket链接池内容:" + webSocketMap.toString());
    }

}